/*+***********************************************************************************
 Filename: A01_tinyriscv_mcu01\src\perips\timer.v
 Description: a simple timer peripheral module
    with  3 I/O registers: ctrl, counter, top.

 Modification:
   2024.02.09 Creation   H.Zheng     

Copyright (C) 2024  Zheng Hui (hzheng@gzhu.edu.cn)

License: MulanPSL-2.0

***********************************************************************************-*/
module timer(

    input wire clk,
    input wire reset_n,

    input wire[31:0] data_i,
    input wire[3:0] addr_i,
    input wire we_i,

    output wire[31:0] data_o,
    output wire int_sig_o

    );

    reg [31:0] timer_ctrl; //bit 0: enable, bit 1: overflow status
    reg [31:0] counter;
    reg [31:0] counter_top;

    always @(posedge clk) begin
        if (reset_n == 1'b0) begin
            counter <= 32'b0;
        end
        else if (we_i &&(addr_i == 4'h1)) begin //load counter initial value
            counter <= data_i;
        end 
        else begin
            if (counter >= counter_top) begin //overflow
                counter <= 32'b0;
            end
            else if (timer_ctrl[0]) begin  //tick counter
                counter <= counter + 1;
            end
        end
    end

    always @(posedge clk) begin
        if (we_i &&(addr_i == 4'h0)) begin //set ctrl reg
            timer_ctrl <= data_i;
            timer_ctrl <= {data_i[31:2], (counter >= counter_top), data_i[0]};
        end 
        else if (we_i &&(addr_i == 4'h2)) begin //set overflow reg
            counter_top <= data_i;
        end 
        else if ((counter >= counter_top) && timer_ctrl[0]) begin //overflow
            timer_ctrl[1] <= 1'b1;
        end
    end

    assign int_sig_o = timer_ctrl[1];

    assign data_o = (addr_i == 4'h0) ? timer_ctrl :
                    (addr_i == 4'h1) ? counter : 
                    (addr_i == 4'h2) ? counter_top : ~0;

/*
    always @ (*) begin
        data_o = counter;
    end
*/    
/*
    localparam REG_CTRL = 4'h0;
//    localparam REG_COUNT = 4'h4;
//    localparam REG_VALUE = 4'h8;
    localparam REG_COUNT = 4'h1;
    localparam REG_VALUE = 4'h2;

    // [0]: timer enable
    // [1]: timer int enable
    // [2]: timer int pending, write 1 to clear it
    // addr offset: 0x00
    reg[31:0] timer_ctrl;

    // timer current count, read only
    // addr offset: 0x04
    reg[31:0] timer_count;

    // timer expired value
    // addr offset: 0x08
    reg[31:0] timer_value;


    assign int_sig_o = ((timer_ctrl[2] == 1'b1) && (timer_ctrl[1] == 1'b1))? `INT_ASSERT: `INT_DEASSERT;

    // counter
    always @ (posedge clk) begin
        if (rst == `RstEnable) begin
            timer_count <= `ZeroWord;
        end else begin
            if (timer_ctrl[0] == 1'b1) begin
                timer_count <= timer_count + 1'b1;
                if (timer_count >= timer_value) begin
                    timer_count <= `ZeroWord;
                end
            end else begin
                timer_count <= `ZeroWord;
            end
        end
    end

    // write regs
    always @ (posedge clk) begin
        if (rst == `RstEnable) begin
            timer_ctrl <= `ZeroWord;
            timer_value <= `ZeroWord;
        end else begin
            if (we_i == `WriteEnable) begin
                case (addr_i[3:0])
                    REG_CTRL: begin
                        timer_ctrl <= {data_i[31:3], (timer_ctrl[2] & (~data_i[2])), data_i[1:0]};
                    end
                    REG_VALUE: begin
                        timer_value <= data_i;
                    end
                endcase
            end else begin
                if ((timer_ctrl[0] == 1'b1) && (timer_count >= timer_value)) begin
                    timer_ctrl[0] <= 1'b0;
                    timer_ctrl[2] <= 1'b1;
                end
            end
        end
    end

    // read regs
    always @ (*) begin
        if (rst == `RstEnable) begin
            data_o = `ZeroWord;
        end else begin
            case (addr_i[3:0])
                REG_VALUE: begin
                    data_o = timer_value;
                end
                REG_CTRL: begin
                    data_o = timer_ctrl;
                end
                REG_COUNT: begin
                    data_o = timer_count;
                end
                default: begin
                    data_o = `ZeroWord;
                end
            endcase
        end
    end
*/

endmodule
